<?php
/**
 * 
 * @author	ronnie  (comdeng@gmail.com)
 * @link		http://fillphp.com/
 * @date		2014-5-1 上午1:13:12
 * @file		Dispatch.php
 * @description 
 */
namespace fillphp;

use fillphp\hook\Hook;
require_once __DIR__.'/page/Response.php';
require_once __DIR__.'/page/Controller.php';
require_once __DIR__.'/hook/Hook.php';

set_exception_handler(function(\Exception $ex){
	header('Content-type: text/plain');
	ob_clean();
	echo "\n\nerror found:";
	echo 
	"\n"."file   :".$ex->getFile().
	"\n"."line   :".$ex->getLine().
	"\n"."message:".$ex->getMessage().
	"\n\n";
});

class DefaultDispatch
{
	const DEFAULT_ACTION_NAME = 'index';
	const CONTROLLER_FILENAME = 'ActionController.php';
	
	function __construct()
	{
		require_once __DIR__.'/hook/Route.php';
		Hook::set(\fillphp\hook\HOOK_REQUEST_ROUTE, '\\fillphp\\hook\\Route::route');
		// \fillphp\hook\Hook::set(\fillphp\hook\HOOK_REQUEST_ROUTE, '\\fillphp\\hook\\RequestHook::Route');
		// \fillphp\hook\Hook::set(\fillphp\hook\HOOK_REQUEST_ROUTE, '\\fillphp\\hook\\RequestHook::Input');
	}
	/**
	 * 分配
	 * @throws \Exception
	 */
	function dispatch()
	{
		require_once __DIR__.'/page/Request.php';
		$request = \fillphp\page\Request::buildFromGlobal();
		
		// 删除系统默认变量，避免在后续调用时使用系统的全局变量
		unset($_POST);
		unset($_GET);
		unset($_SERVER);
		unset($_COOKIE);
		unset($_FILES);
		
		
		$arr = array_diff(explode('/', rtrim( ltrim(strtolower($request->url), '/'), '/') ), array(''));
		$len = count($arr);
		$pos = $len - 1;
		$args = array();
		$actionName = self::DEFAULT_ACTION_NAME;
		while($pos >= 0) {
			$last = $arr[$pos];
			if ($last == self::DEFAULT_ACTION_NAME) {
				$tmp = array_pop($arr);
				$file = sprintf('%s/%s/%s', PAGE_ROOT, implode('/', $arr), self::CONTROLLER_FILENAME);
				if (is_readable($file)) {
					break;
				}
			} elseif ( preg_match('#^[1-9][0-9]+$#', $last) ) {
				array_unshift($args, $last);
				array_pop($arr);
				$actionName = self::DEFAULT_ACTION_NAME;
			} elseif (preg_match('#^([a-z0-9]{32}|[a-z0-9]{40})$#', $last)) {
				array_unshift($args, $last);
				array_pop($arr);
				$actionName = self::DEFAULT_ACTION_NAME;
			} elseif($actionName == self::DEFAULT_ACTION_NAME) {
				$file = sprintf('%s/%s/%s', PAGE_ROOT, implode('/', $arr), self::CONTROLLER_FILENAME);
				if (is_readable($file)) {
					break;
				}
			} else {
				$actionName = array_pop($arr);
				$file = sprintf('%s/%s/%s', PAGE_ROOT, implode('/', $arr), self::CONTROLLER_FILENAME);
				if (!is_readable($file)) {
					throw new \Exception(__CLASS__.':controllerNotFound file:'. $file);
				}
				break;
			}
			$pos--;
		}
		if ($pos < 0) {
			$file = sprintf('%s/%s', PAGE_ROOT, self::CONTROLLER_FILENAME);
			if (!is_readable($file)) {
				throw new \Exception(__CLASS__.':controllerNotFound file:'.$file);
			}
		}
		
		require_once $file;
		$actionName = implode('', array_map('ucfirst', explode('_', $actionName))).'Action';
		$className = sprintf('%s\%s%s', NAMESPACE_ROOT.'\\page', ($arr ? implode("\\", $arr)."\\" : ''), $actionName);
		if (!class_exists($className)) {
			throw new \Exception(__CLASS__.':classNotDefined class:'.$className);
		}
		$action = new $className();
		$reqMethod = strtolower($request->method);
		if (!is_callable(array($action, $reqMethod))) {
			throw new \Exception(__CLASS__.':methodNotCallable method:'.$reqMethod);
		}
		
		$argsRefMethod = $reqMethod;
		// 如果调用get方法，需要依赖于head方法获取返回的Response的状态码
		if ($reqMethod == 'get') {
			$argsRefMethod = 'head';
			if (!is_callable(array($action, $argsRefMethod))) {
				throw new \Exception(__CLASS__.':headMethodMissed detail:head method is required by get to detain whether to show the page');
			}
		}
		
		// 构建传入的参数, 优先使用和参数中同名的gets变量，而不是通过url传入的参数
		$refMethod = new \ReflectionMethod($action, $argsRefMethod);
		$params = $refMethod->getParameters();
		$ps = array();
		foreach($params as $key => $param) {
			$name = $param->getName();
			if (isset($request->inputs['gets'][$name])) {
				$ps[] = $request->get($name);
			} else {
				$ps[] = null;
			}
		}
		$ps = array_replace($ps, $args);
		// 设置action的request对象
		$request->inputs['args'] = $args;
		$action->setRequest($request);
		
		$response = $refMethod->invokeArgs($action, $ps);
		if (!$response instanceof \fillphp\page\Response) {
			throw new \Exception(__CLASS__.'\responseClassIllegal');
		}
		if ($argsRefMethod != $refMethod) {
			if ($response->code == \fillphp\page\STATUS_OK) {
				$response = call_user_func_array(array($action, $reqMethod), array());
				if (!$response instanceof \fillphp\page\Response) {
					throw new \Exception(__CLASS__.':responseClassIllegal');
				}
				$response->prepare($request)->send();
			} else {
				$time = date('Y/m/d H:i:s');
				$response->content = <<<HTML
<h1>{$response}</h1>
<hr/>
<h3>您访问的页面发生错误了，您可以：</h3>
<ol>
	<li><a href="{$request->request_uri}">刷新</a></li>
	<li><a href="/">返回首页</a></li>
</ol>
<em>{$time}</em>
HTML;
				$response->prepare($request)->send();
			}
		} else {
			$response->prepare($request)->send();
		}
	}
}